/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * Projects
 * Copyright (C) Arnaud Duval 2010 <arnaud.duval@gmail.com>
 * 
 * Projects is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Projects is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ActivityHistory.hpp>
#include <fstream>
#include <config.hpp>
#include <filesystem-utils.hpp>
#include <Duration.hpp>
#include <assert.h>

///@brief Default constructor
ActivityHistory::ActivityHistory()
{

}

///@brief Copy constructor
///@brief h ActivityHistory object to copy
ActivityHistory::ActivityHistory(const ActivityHistory& h)
{
    trackPoints = h.trackPoints;
}

///@brief Destructor
ActivityHistory::~ActivityHistory()
{

}

///@brief Overoad of = operator
///@param h ActivityHistory object to copy
ActivityHistory& ActivityHistory::operator=(const ActivityHistory& h)
{
    trackPoints = h.trackPoints;
    return (*this);
}

///@brief Read history from a XML node in a TCX file
///@param activityNode XML node to parse
///@param id Id of the activity to read
void ActivityHistory::ReadFromTcxFile(xmlpp::Node* activityNode, std::string id)
{
    if(activityNode->get_name() == "Activity")
    {
        std::string currentId;
        // Parsing activity node children
        xmlpp::Node::NodeList activityChildren = activityNode->get_children();
        for (xmlpp::Node::NodeList::iterator iterActivityChildren = activityChildren.begin();
             iterActivityChildren != activityChildren.end();
             ++iterActivityChildren)
        {
            xmlpp::Node* activityChildNode = dynamic_cast<xmlpp::Node*>(*iterActivityChildren);
            if(activityChildNode->get_name() == "Id")
            {
                currentId = ((xmlpp::TextNode*) (*((activityChildNode->get_children()).begin())))->get_content();
            }
            if((activityChildNode->get_name() == "Lap")&&(currentId == id))
            {
                // Parsing lap node children
                xmlpp::Node::NodeList lapNodeChildren = activityChildNode->get_children();
                for(xmlpp::Node::NodeList::iterator iterLapNodeChildren = lapNodeChildren.begin();
                    iterLapNodeChildren != lapNodeChildren.end();
                    ++iterLapNodeChildren)
                {
                    xmlpp::Node* lapNodeChild = dynamic_cast<xmlpp::Node*>(*iterLapNodeChildren);
                    if(lapNodeChild->get_name() == "Track")
                    {
                        //Parsing track node childen
                        xmlpp::Node::NodeList trackNodeChildren = lapNodeChild->get_children();
                        for (xmlpp::Node::NodeList::iterator iterTrackNodeChildren = trackNodeChildren.begin();
                             iterTrackNodeChildren != trackNodeChildren.end();
                             ++iterTrackNodeChildren)
                        {
                            xmlpp::Node* trackNodeChild = dynamic_cast<xmlpp::Node*>(*iterTrackNodeChildren);
                            if(trackNodeChild->get_name() == "Trackpoint")
                            {
                                RichTrackPoint newPoint;
                                newPoint.ReadFromTcxFile(trackNodeChild);
                                trackPoints.push_back(newPoint);
                            }
                        }
                    }
                }
                
            }
        }
    }
}


///@brief Write a file containing the history
///@param shortName Name of the file without extension
///@param dirPath Path to the directory where the file should be written
bool ActivityHistory::WriteHistoryFile(std::string shortName, std::string dirPath)
{
    //shortName should be corrected if it contains ":" character
    size_t pos = shortName.find(":");
    while(pos != std::string::npos)
    {
        shortName.replace(pos, 1, "-");
        pos = shortName.find(":");
    }
    
    std::ofstream fout((dirPath+TRAININGBOOK_FILE_SEPARATOR+shortName+".history").c_str());
    if(fout.fail())
    {
        std::cerr << "Error while opening file : " << (dirPath+TRAININGBOOK_FILE_SEPARATOR+shortName+".history").c_str() << "\n"; 
        return false;
    }
    fout << "<ActivityHistory>\n";
    for(std::vector<RichTrackPoint>::iterator iter = trackPoints.begin();
        iter != trackPoints.end(); ++iter)
    {
        fout << "   <TrackPoint>\n";
        (*iter).WriteXML(fout);
        fout << "   </TrackPoint>\n";
    }
    
    fout << "</ActivityHistory>\n";
    
    fout.close();
    return true;
}

///@brief Read a file containing the history
///@param shortname Name of the file without extension
///@param dirPath Path to the directory where the file should be read
bool ActivityHistory::ReadHistoryFile(std::string shortName, std::string dirPath)
{
    //shortName should be corrected if it contains ":" character
    size_t pos = shortName.find(":");
    while(pos != std::string::npos)
    {
        shortName.replace(pos, 1, "-");
        pos = shortName.find(":");
    }
    shortName = shortName + ".history";

    if(!FileExists(dirPath+TRAININGBOOK_FILE_SEPARATOR+shortName))
        return false;
    
    try
    {
        xmlpp::DomParser parser;
        parser.set_substitute_entities();
        parser.parse_file(dirPath+TRAININGBOOK_FILE_SEPARATOR+shortName);
        if(parser)
        {
            //Walk the tree
            const xmlpp::Node* pNode = parser.get_document()->get_root_node();
            if(pNode->get_name() == "ActivityHistory")
            {
                xmlpp::Node::NodeList historyChildren = pNode->get_children();
                for(xmlpp::Node::NodeList::iterator iterHistoryChildren = historyChildren.begin();
                    iterHistoryChildren != historyChildren.end() ; ++iterHistoryChildren)
                {
                    xmlpp::Node* historyChild = dynamic_cast<xmlpp::Node*>(*iterHistoryChildren);
                    if(historyChild->get_name() == "TrackPoint")
                    {
                        RichTrackPoint newPoint;
                        newPoint.ReadHistoryFile(historyChild);
                        trackPoints.push_back(newPoint);
                    }
                }
            }
        }
    }
    catch(const std::exception& ex)
    {
        std::cerr << "Exception caught: " << ex.what() << std::endl;
    }    
    return true;
}

///@brief Return a list of DateTime for each recording
std::list<DateTime> ActivityHistory::GetDateTimeList()
{
    std::list<DateTime> res;
    for(std::vector<RichTrackPoint>::iterator iter = trackPoints.begin() ; iter != trackPoints.end() ; ++iter)
    {
        res.push_back((*iter).GetDateTime());
    }
    
    return res;
}

///@brief Compute the average power
double ActivityHistory::ComputeAveragePower()
{
    ///@todo Make tests with tracks that have stops or automatic recording
    double sumEnergy = 0.;
    double sumTime = 0.;
    RichTrackPoint p1, p2;
    for(std::vector<RichTrackPoint>::iterator trackit = trackPoints.begin() ; trackit != trackPoints.end() ; ++trackit)
    {
        if(trackit == trackPoints.begin())
        {
            p1 = (*trackit);
        }
        else
        {
            p2 = (*trackit);
            
            Duration duration;
            duration.Difference(p2.GetDateTime(),p1.GetDateTime());
            sumTime += duration.ValueInSeconds();
            sumEnergy += ((p2.GetPower()+p1.GetPower())/2.)*duration.ValueInSeconds();
            p1 = p2;
        }
        
        
    }
    
    return sumEnergy / sumTime;   
}

///@brief Return the number of laps
unsigned int ActivityHistory::GetNumberOfLaps()
{
    //return laps.size();
    ///@todo Rewrite this function with new definition of intervals
    return 0;
}

/*///@brief Return a given lap history
///@param palidx Index of returned lap
LapHistory ActivityHistory::GetLapHistory(unsigned int lapidx)
{
    assert(lapidx >= 0);
    assert(lapidx < laps.size());
    return laps[lapidx];
}*/
